package com.anis.project.medcare.editors;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.wb.swt.SWTResourceManager;

import com.anis.calendar.ui.widgets.calendar.swt.renderer.AppointmentRenderer;
import com.anis.calendar.ui.widgets.calendar.swt.renderer.CalendarGridRenderer;
import com.anis.calendar.ui.widgets.calendar.swt.renderer.CalendarTimeScaleRenderer;
import com.anis.calendar.ui.widgets.calendar.swt.renderer.DayHeaderRenderer;
import com.anis.calendar.ui.widgets.model.Appointment;
import com.anis.calendar.ui.widgets.model.AppointmentModificator;
import com.anis.calendar.ui.widgets.model.CalendarIntervalFilter;
import com.anis.calendar.ui.widgets.model.CalendarModel;
import com.anis.calendar.ui.widgets.model.Day;
import com.anis.calendar.ui.widgets.model.ModelCreator;
import com.anis.calendar.ui.widgets.util.ui.timebars.TimeBarViewerDelegate;
import com.anis.calendar.ui.widgets.util.ui.timebars.TimeBarViewerInterface;
import com.anis.calendar.ui.widgets.util.ui.timebars.model.DefaultTimeBarRowModel;
import com.anis.calendar.ui.widgets.util.ui.timebars.model.TimeBarModel;
import com.anis.calendar.ui.widgets.util.ui.timebars.model.TimeBarRow;
import com.anis.calendar.ui.widgets.util.ui.timebars.strategy.OverlapInfo;
import com.anis.calendar.ui.widgets.util.ui.timebars.swt.TimeBarViewer;
import com.anis.project.medcare.Activator;
import com.anis.project.medcare.search.dialog.AppointementDialog;

import de.jaret.util.date.Interval;
import de.jaret.util.date.JaretDate;

public class RdvEditor extends EditorPart {

	public static String ID = "com.anis.project.medcare.rdveditor";
	private Point _dragStart;
	private int _startOffsetX;
	private int _startOffsetY;
	private de.jaret.util.date.JaretDate _dragStartDate;
	private List<Interval> _draggedIntervals;
	private List<Integer> _yOffsets;
	private List<Interval> _origIntervals;
	private List<TimeBarRow> _origRows;
	private TimeBarRow _draggedRow;
	private boolean _isRowDrag;
	int _lastDragOverX;
	int _lastDragOverY;
	private TimeBarViewer timeBar1;
	private Day date;
	private MenuItem newAppointement;
	private MenuItem editAppointement;

	public RdvEditor() {
		// TODO Auto-generated constructor stub
	}

	@Override
	public void doSave(IProgressMonitor monitor) {
		// TODO Auto-generated method stub

	}

	@Override
	public void doSaveAs() {
		// TODO Auto-generated method stub

	}

	@Override
	public void init(IEditorSite site, IEditorInput input)
			throws PartInitException {
		setSite(site);
		setInput(input);
	}

	@Override
	public boolean isDirty() {
		return false;
	}

	@Override
	public boolean isSaveAsAllowed() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void createPartControl(Composite parent) {
		parent.setLayout(new GridLayout(1, false));

		Label lblCalendar = new Label(parent, SWT.CENTER);
		lblCalendar.setFont(SWTResourceManager.getFont("Trebuchet MS", 12,
				SWT.BOLD));
		lblCalendar.setForeground(SWTResourceManager
				.getColor(SWT.COLOR_DARK_BLUE));
		lblCalendar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false,
				1, 1));
		lblCalendar.setText("Calendar");

		Group calandarGroup = new Group(parent, SWT.NONE);

		calandarGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
				true, 1, 1));

		createTimeBar(calandarGroup);

	}

	@Override
	public void setFocus() {
		// TODO Auto-generated method stub

	}

	private void createTimeBar(Group parent) {
		GridLayout gridLayout = new GridLayout();
		gridLayout.numColumns = 1;
		parent.setLayout(gridLayout);

		TimeBarModel model = Activator.getDefault().getModel();
		if (model == null) {
			int year = Calendar.getInstance().get(Calendar.YEAR);
			model = ModelCreator.createCalendarModel(year);
		}

		GridData gd = new GridData(GridData.FILL_BOTH);

		timeBar1 = new TimeBarViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL);
		timeBar1.setLayoutData(gd);

		timeBar1.setTimeScalePosition(TimeBarViewer.TIMESCALE_POSITION_TOP);
		timeBar1.setModel(model);
		timeBar1.setAutoScaleRows(7);
		timeBar1.setPixelPerSecond(0.018);
		timeBar1.setDrawRowGrid(true);

		timeBar1.setSelectionDelta(6);
		// this is the col width!
		timeBar1.setRowHeight(150);

		timeBar1.setTBOrientation(TimeBarViewerInterface.Orientation.VERTICAL);
		// vertical: the y axiswidth is the height of the row headers!
		timeBar1.setYAxisWidth(20);

		// do not adjust the displaed time according to the model
		// use the basedate day!
		timeBar1.setAdjustMinMaxDatesByModel(true);
		timeBar1.setMinDate(CalendarModel.BASEDATE.copy());
		timeBar1.setMaxDate(CalendarModel.BASEDATE.copy().advanceDays(1));

		// set the header renderer
		timeBar1.setHeaderRenderer(new DayHeaderRenderer());

		// timescale is default + special tooltip
		timeBar1.setTimeScaleRenderer(new CalendarTimeScaleRenderer());

		// register the AppointmentRenderer for rendering appointments
		AppointmentRenderer ar = new AppointmentRenderer();
		timeBar1.registerTimeBarRenderer(Appointment.class, ar);

		// modifications are restricted
		timeBar1.addIntervalModificator(new AppointmentModificator());

		timeBar1.setGridRenderer(new CalendarGridRenderer());

		// CalendarControlPanel ctrl = new CalendarControlPanel(parent,
		// SWT.NULL,
		// timeBar1, null);
		// gd = new GridData(GridData.FILL_HORIZONTAL);
		// ctrl.setLayoutData(gd);

		// add a listener for doubleclicks
		// shows how to get intervals from a point
		timeBar1.addMouseListener(new MouseAdapter() {
			public void mouseDoubleClick(MouseEvent e) {
				List<Interval> intervals = timeBar1.getIntervalsAt(e.x, e.y);
				if (intervals != null && intervals.size() > 0) {
					for (Interval interval : intervals) {
						System.out.println("Doubleclicked: "
								+ interval.toString());
					}
				}
			}
		});

		timeBar1.addSelectionChangedListener(new ISelectionChangedListener() {

			@Override
			public void selectionChanged(SelectionChangedEvent event) {
				StructuredSelection selection = (StructuredSelection) event
						.getSelection();
				if (selection.getFirstElement() instanceof Day) {
					date = (Day) selection.getFirstElement();
					if (date != null) {
						Menu popupMenu = new Menu(timeBar1);
						timeBar1.setMenu(popupMenu);
						newAppointement = new MenuItem(popupMenu, SWT.CASCADE);
						newAppointement.setText("New Appointement");
						editAppointement = new MenuItem(popupMenu, SWT.NONE);
						editAppointement.setText("Edit Appointement");
						newAppointement
								.addSelectionListener(new SelectionListener() {
									@Override
									public void widgetSelected(SelectionEvent e) {
										createAppoinementWizard(date);
									}

									@Override
									public void widgetDefaultSelected(
											SelectionEvent e) {
									}
								});
					}
				}

			}

		});

		initDND(timeBar1, parent);
		int toDay = Calendar.getInstance().get(Calendar.DATE);
		int thisMonth = Calendar.getInstance().get(Calendar.MONTH);
		int thisYear = Calendar.getInstance().get(Calendar.YEAR);
		JaretDate date = new JaretDate(toDay, thisMonth, thisYear, 0, 0, 0);
		timeBar1.setIntervalFilter(new CalendarIntervalFilter());
		timeBar1.setYAxisWidth(50);
	}

	protected void createAppoinementWizard(Day date2) {
		Shell parentShell = Activator.getDefault().getWorkbench()
				.getActiveWorkbenchWindow().getShell();
		AppointementDialog newAppointementDialog = new AppointementDialog(
				parentShell, date2, timeBar1);
		newAppointementDialog.open();
//		try {
//			savetimeBarModeltoDB(timeBar1.getModel());
//		} catch (Exception e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
	}

	private void savetimeBarModeltoDB(TimeBarModel model) throws Exception {
		Activator.getDefault().calendarHandler().writeJavaObject(model);
	}

	private void initDND(final TimeBarViewer tbv, Composite parent) {
		// support move and copy
		int operations = DND.DROP_COPY | DND.DROP_MOVE;
		final DragSource source = new DragSource(tbv, operations);

		// Provide data in Text format
		Transfer[] types = new Transfer[] { TextTransfer.getInstance() };
		source.setTransfer(types);

		source.addDragListener(new DragSourceListener() {
			public void dragStart(DragSourceEvent event) {
				boolean horizontal = timeBar1.getTBOrientation().equals(
						TimeBarViewerInterface.Orientation.HORIZONTAL);
				TimeBarViewerDelegate delegate = (TimeBarViewerDelegate) timeBar1
						.getData("delegate");

				// check whether drag occured on the drag marker in the interval
				if (timeBar1.isInDiagram(event.x, event.y)) {
					List<Interval> l = tbv.getIntervalsAt(event.x, event.y);
					if (l.size() > 0) {
						Interval interval = l.get(0);

						TimeBarRow clickedRow = timeBar1.getModel()
								.getRowForInterval(interval);
						OverlapInfo oi = delegate.getOverlapStrategy()
								.getOverlapInfo(clickedRow, interval);
						boolean overlap = oi != null ? oi.overlappingCount > 0
								: false;

						Rectangle bounds = timeBar1.getIntervalBounds(interval);
						if (AppointmentRenderer.isInDragMark(bounds, event.x,
								event.y, horizontal, overlap)) {
							// remember the point the drag started
							_dragStart = new Point(event.x, event.y);
							_dragStartDate = timeBar1.dateForXY(event.x,
									event.y);
							_isRowDrag = false;
							// make sure the interval is selected
							timeBar1.getSelectionModel().addSelectedInterval(
									interval);

							// capture the data of the dragged intervals
							// drag all selected intervals
							// to allow changing the time of the intervals and
							// drawing ghost intervals a copy of the
							// intervals will be made
							_draggedIntervals = new ArrayList<Interval>();
							_origIntervals = new ArrayList<Interval>();
							_origRows = new ArrayList<TimeBarRow>();
							_yOffsets = new ArrayList<Integer>();

							for (Interval i : timeBar1.getSelectionModel()
									.getSelectedIntervals()) {
								_draggedIntervals.add(((Appointment) i).copy());
								TimeBarRow row = timeBar1.getModel()
										.getRowForInterval(i);
								_origIntervals.add(i);
								_origRows.add(row);

								int yOffset;
								if (timeBar1
										.getTBOrientation()
										.equals(TimeBarViewerInterface.Orientation.HORIZONTAL)) {
									yOffset = timeBar1.getYForRow(row)
											- _dragStart.y;
								} else {
									yOffset = timeBar1.getYForRow(row)
											- _dragStart.x;
								}
								_yOffsets.add(yOffset);
							}

							TimeBarRow row = timeBar1.getRowForXY(event.x,
									event.y);
							// store both x and y offsets since the orientation
							// may be vertical or horizontal
							_startOffsetY = event.y - tbv.getYForRow(row);
							_startOffsetX = event.x - tbv.getYForRow(row);
							return;
						}
					}
				}
				event.doit = false;
			}

			public void dragSetData(DragSourceEvent event) {
				// Provide the data of the requested type.
				if (TextTransfer.getInstance().isSupportedType(event.dataType)) {
					// unfortenatly the coordinates in the event are always 0,0
					// -> use the coordinates transferred in
					// startDrag to get the interval (a possibly is to use the
					// selection, especially for dragging
					// multiple intervals)
					if (_isRowDrag) {
						event.data = "row: "
								+ _draggedRow.getRowHeader().getLabel();
					} else {
						StringBuffer buf = new StringBuffer();
						buf.append("intervals:");
						for (Interval interval : _origIntervals) {
							buf.append(interval);
							buf.append(";");
						}
						event.data = buf.toString();
					}
				}
			}

			public void dragFinished(DragSourceEvent event) {
				// drag finished -> if it is a move operation remove the
				// original intervals
				// from their rows
				if (event.detail == DND.DROP_MOVE) {
					System.out.println("Perform move");
					for (Interval interval : _origIntervals) {
						DefaultTimeBarRowModel row = (DefaultTimeBarRowModel) timeBar1
								.getModel().getRowForInterval(interval);
						row.remInterval(interval);
					}
				}
				// clear the dragged data
				// and the ghosts in the viewer
				timeBar1.deHighlightRow();
				timeBar1.setGhostIntervals(null, null);
				timeBar1.setGhostRows(null, null);
				_draggedIntervals = null;
				_draggedRow = null;
				_isRowDrag = false;
				_origIntervals = null;
				_origRows = null;
			}
		});

		// ////////////////////
		// Drop target

		// Allow data to be copied or moved to the drop target
		operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT;
		final DropTarget target = new DropTarget(tbv, operations);

		// Receive data in Text
		final TextTransfer textTransfer = TextTransfer.getInstance();
		types = new Transfer[] { textTransfer };
		target.setTransfer(types);

		target.addDropListener(new DropTargetListener() {
			public void dragEnter(DropTargetEvent event) {
			}

			public void dragOver(DropTargetEvent event) {
				// event.feedback = DND.FEEDBACK_SELECT | DND.FEEDBACK_SCROLL;

				// prevent multiple dragOvers without move
				if (event.x != _lastDragOverX || event.y != _lastDragOverY) {
					_lastDragOverX = event.x;
					_lastDragOverY = event.y;

					// System.out.println("dragover " + event);

					if (!_isRowDrag && _draggedIntervals != null
							&& _draggedIntervals.size() > 0) {
						// map the coordinates from the event to the timebar
						// viewer widget
						int destY = Display.getCurrent().map(null, tbv,
								event.x, event.y).y;
						int destX = Display.getCurrent().map(null, tbv,
								event.x, event.y).x;

						de.jaret.util.date.JaretDate curDate = timeBar1
								.dateForXY(destX, destY);
						long diffSeconds = _dragStartDate.diffSeconds(curDate);
						// correct the dragged interval bounds
						for (int i = 0; i < _draggedIntervals.size(); i++) {
							Interval orig = _origIntervals.get(i);
							Interval interval = _draggedIntervals.get(i);
							interval.setBegin(orig.getBegin().copy()
									.advanceSeconds(-diffSeconds));
							interval.setEnd(orig.getEnd().copy()
									.advanceSeconds(-diffSeconds));
						}

						// check if we are over a row and highlight it
						// this will only highlight the row for the mouse
						// pointer
						// if multiple intervals are dragged their respective
						// rows will not be highlighted
						TimeBarRow overRow = tbv.getRowForXY(destX, destY);
						if (overRow != null) {
							tbv.highlightRow(overRow);
						} else {
							System.out.println("Row is null x,y:" + event.x
									+ "," + event.y);
							tbv.deHighlightRow();
						}
						// tell the timebar viewer
						tbv.setGhostIntervals(_draggedIntervals, _yOffsets);
						tbv.setGhostOrigin(destX, destY);
					}
				}
			}

			public void dragOperationChanged(DropTargetEvent event) {
			}

			public void dragLeave(DropTargetEvent event) {
				// leaving: do not leave a row highlighted
				tbv.deHighlightRow();
				tbv.setGhostIntervals(null, null);
			}

			public void dropAccept(DropTargetEvent event) {
			}

			public void drop(DropTargetEvent event) {
				// drop operation. the drop operation places the dragged
				// intervals in the destination rows.
				// if a row can not be determined (no row under the dragged
				// interval) the interval will be discarded
				if (textTransfer.isSupportedType(event.currentDataType)) {
					String text = (String) event.data;
					System.out.println("DROP: " + text);

					if (_draggedIntervals != null) {
						for (int i = 0; i < _draggedIntervals.size(); i++) {
							int destY = Display.getCurrent().map(null, tbv,
									event.x, event.y).y;
							int destX = Display.getCurrent().map(null, tbv,
									event.x, event.y).x;
							int offY = _yOffsets.get(i);
							TimeBarRow overRow = null;
							if (timeBar1
									.getTBOrientation()
									.equals(TimeBarViewerInterface.Orientation.HORIZONTAL)) {
								overRow = tbv.rowForY(destY + offY
										+ _startOffsetY);
							} else {
								overRow = tbv.rowForY(destX + offY
										+ _startOffsetX);
							}
							DefaultTimeBarRowModel row = (DefaultTimeBarRowModel) overRow;

							// NOTE: copies are placed in the dest rows
							if (overRow != null) {
								row.addInterval(_draggedIntervals.get(i));
								Appointment app = (Appointment) _draggedIntervals
										.get(i);
								Day destDay = (Day) overRow;
								de.jaret.util.date.JaretDate realBegin = app
										.getRealBegin().copy();
								realBegin.setDate(
										destDay.getDayDate().getDay(), destDay
												.getDayDate().getMonth(),
										destDay.getDayDate().getYear());
								app.setRealBegin(realBegin);
								de.jaret.util.date.JaretDate realEnd = app
										.getRealEnd().copy();
								realEnd.setDate(destDay.getDayDate().getDay(),
										destDay.getDayDate().getMonth(),
										destDay.getDayDate().getYear());
								app.setRealEnd(realEnd);

							}
						}
					}
				}
			}

		});

		// Dispose listener on parent of timebar viewer to dispose the
		// dragsource and dragtarget BEFORE the timebar
		// viewer
		// this prevents an exception beeing thrown by SWT
		parent.addDisposeListener(new DisposeListener() {
			public void widgetDisposed(DisposeEvent e) {
				source.dispose();
				target.dispose();
			}
		});

	}

}
